如何用 R 绘制交互式社会网络图?
挖掘社会关系网络,助你洞若观火。
需求
最近有个学生问我,如何绘制交互式社会网络图(Interactive Social Network Graph)?
之所以一定要交互式,是因为他的应用场景,是演示给客户。
他解释说,如果客户有选项,可以根据需要来缩放图形、聚焦类别,甚至是可以拖拽图形元素,以不同视角来查看,那展示效果显然会更好。
我对学生这次把握客户需求的能力,还是比较赞赏的。确实,数据可视化的目的就是为了与人沟通。而人是需要控制感的。
在数据科学里,这个需求,属于网络可视化(Network Visualization)范畴。应用上,除了描绘社会网络外,这种可视化的对象还可以包括引文网络、贸易网络、信息(或疾病)传播网络等。甚至,网络可视化操作还能与时序数据结合,例如 Maximilian Noichl 绘制的这张古代哲学家的动态关系网络图。
网络可视化的工具,是非常多的。
然而,一旦涉及了交互式,选项就大幅减少了。特别是,他还要求免费、易学、格式开放、演示环境易于部署……
这条件,够苛刻了吧?
还好,我还真就帮他找到了。
虽然易学,可是由于他是文科生,我觉得还是给他写个教程,比较妥帖。
教程写完之后,我觉得你可能也会用得上,所以一并分享给你。
演示数据,我自然不能用他的。否则有泄露商业机密的风险。这里我采用的,是斯坦福大学开放课程《数据库基础》中的一个简单数据样例。我在国际班讲的英文数据库课程,一直用它作为练习数据。
这个演示数据的特点,就是简单。
有多简单?往下看。
数据
该数据刻画的,是某高中里4个年级部分学生之间的社交关系。
我们一共需要用到3张数据表格,分别是学生信息、朋友关系和“喜欢”关系。
先看学生信息表。我已经把它上传到了这个 github 链接,你可以点击查看。
表格包含3列,分别是学号、姓名和年级。
然后是朋友关系表。请点击这个链接查看。
除去表头,一共20行。包含两列,代表了关系两端的两个学生的学号。
注意“朋友”关系是一种双向(或者无向)关系。两个人都认可的,才算是朋友。我在人群中,一眼就能认出某影视巨星。可惜,人家不认识我,这显然就不算朋友关系。
再后面,是“喜欢”(likes)关系表。我放在了这个链接。
注意这里虽然也描述了关系的起点和终点,但是“喜欢”关系是一种有向关系。张三喜欢李四,李四可能并不喜欢张三。
好了,这就是我们需要用到的全部数据了。下面我们来看看运行环境。
环境
我们使用的,是 R 的集成开发环境(integrated development environment, IDE) RStudio 。
你可以在本机安装 R 以及 RStudio 。我已经把安装和设置步骤写在了《如何用 R 快速了解科研领域?》一文中。
配套的代码和数据,我放在了这个 github 仓库中。你可以下载使用。
然而,此处为了你更快捷地上手,我直接为你准备了云端的运行环境。你只需要点击这个链接(http://t.cn/EJ5U8o6),就可以直接在浏览器里面开启一个 RStudio 。其中,已包含了我为你准备好的数据和代码。
双击右下方目录区域里面的 demo.Rmd
,你就能看到对应的笔记本了。
怎么样?够方便吧?
其实,你自己也可以构建这样的云端环境。具体的方法,我已经在《如何用iPad运行Python代码?》一文中为你详细介绍了。如果你感兴趣,可以在学过本篇教程后,尝试练练手。
下面我们开始介绍代码了。请你根据我的介绍,逐步点击代码模块旁边的运行按钮,查看运行的结果。
代码
首先,我们需要读入本教程中最重要的软件包,也就是 R 环境下的网络交互可视化工具—— visNetwork。
visNetwork 基于 Javascript 可视化工具库 vis.js 开发,为 R 用户提供了简单易用的界面,而且功能也很强大。
我们使用 library
命令来读入它。
library('visNetwork')
下面我们来读入数据。
首先,我们观察“朋友”关系。读入学生信息表和朋友关系表。
nodes <- read.csv("data/Highschooler.csv", header=T, as.is=T)
friends <- read.csv("data/Friend.csv", header=T, as.is=T)
注意这里的参数:
header=T
是指我们读入的数据里包含了表头,需要让 R 注意,不要把表头也当成了数据来处理;as.is=T
是指读入的字符串数据,不要默认按照因子来转换。这其实是一种技巧。因为如果大量数据默认做转换,可能会导致读取效率很低。当然,对于我们的例子来说,因为数据量很小,实际上效率差别不大。但是好习惯还是需要养成的。
下面我们要在节点上生成一些属性。我们希望把鼠标挪到某个节点上的时候,显示该学生属于“某年级”;而生成图像的时候,直接在节点旁边标明学生的姓名。
因此,我们就需要分别对 nodes$title
和 nodes$label
赋值。
之后,用 visNetwork
可视化我们的节点和关系连线。
nodes$title <- paste0("grade", nodes$grade)
nodes$label <- nodes$name
visNetwork(nodes, friends)
结果如下图所示:
注意,这可不是一张静态图,你把鼠标悬浮到 Alexis 人名上试试看。
年级属性就出现了。
你还可以拖动任意一个学生节点,感受一下什么叫做“牵一发而动全身”。
有趣吧?
但是现在所有的节点,都是一样的颜色。我们希望依据不同的年级,重新绘制节点颜色,这样看得会更清晰。
我们依据年级属性(nodes$grade
),来定义节点的背景颜色(nodes$color.background
)。
然后再次调用 visNetwork
,进行绘制。
nodes$color.background <- c("orange", "pink", "yellow", "blue")[nodes$grade]
visNetwork(nodes, friends)
结果是……
不对呀,说好的颜色变化呢?
别着急。
如果让 R 根据不同属性来区分颜色,我们首先需要保证该属性类型是因子(factor)。可是我们读取的时候,为了效率,没让 R 自动转换。
怎么办?
手动来做吧。
nodes$grade <- as.factor(nodes$grade)
nodes$color.background <- c("orange", "pink", "yellow", "blue")[nodes$grade]
visNetwork(nodes, friends)
这次,我们再看看结果:
嗯,感觉好极了。
这里节点很少,全部同时显示,也能看得清晰。但是假设我们需要处理一所真正学校中的朋友关系,可以想象那会有成百上千个节点。如果我们希望聚焦,那就得给用户更多的交互功能。
这里我给你介绍其中一个选项,就是利用 selectedBy
,指定我们让用户在哪一个属性上进行分组选择。
你需要把它放在 visOptions
中。
visNetwork(nodes, friends) %>%
visOptions(selectedBy = "grade")
运行效果是这样的:
看起来,跟刚刚区别不大,只是多了一个下拉框而已。
我们尝试选择一下:
交互选项,让当前分组保持高亮,其他分组变灰暗,于是我们的注意力就可以集中。
尝试着玩儿一下,看看你能否发现什么有趣的关系模式?
我发现了一个。
大部分情况下,同一个年级的学生间,总会可以关联起来。但是,11年级的 Austin ,跟本年级的其他3名同学,没有任何直接联系。反而,跟其他年级的学生,保持了朋友关系。
如果你是他的班主任,要不要关注一下?
下面,我们来看看另外一种关系——“喜欢”(likes)。
还是读入 csv 文件,到 likes
数据框里面。参数跟刚才一样。
likes <- read.csv("data/Likes.csv", header=T, as.is=T)
先来绘制看看。
visNetwork(nodes, likes)
我们一下子就发现了,这次的整体图形,不再是全连通的。出现了孤立节点。
这些人,既没有“喜欢”别人,也没有“被喜欢”。你自己找找看,都包括哪几个学生?
注意这个图形,是有问题的。
前面提到过,“喜欢”关系是一种有向关系。因此关系的方向很重要。但是目前这张图里面,方向是缺失的。
没关系,只需要给 likes
加入一个属性 arrow
就好。
likes$arrows <- "to"
visNetwork(nodes, likes)
这次就对了。
下面我们来玩儿一个更有趣的——把两个关系合并,一起观察。
我们需要记录一下,关系来自于哪张关系表格。所以分别把表名称作为属性,填到 relation
字段中。
friends$relation <- c("friend")
likes$relation <- c("likes")
看看这时候的 friends
朋友关系表。
friends
好了,下面我们需要合并两张表格。我们需要用到 tidyverse
软件包中的按行合并(bind_rows
)功能。所以,需要首先载入tidyverse
软件包。
library(tidyverse)
然后,把原先的两张关系表,合并成一张,起名叫做 links
。
links <- bind_rows(friends, likes)
links
再次绘制,注意这次,关系位置上,我们放的是合并的 links
。
visNetwork(nodes, links)
两种关系,确实都绘制好了。可问题是,关系展示的颜色是一样的蓝色,看着不是很清晰。
仿照刚才对节点的颜色赋值,我们把关系连接的颜色也设置一下。
links$color <- c("green", "red")[links$relation]
visNetwork(nodes, links)
呃……好像没有变化嘛。
聪明的你,一定可以举一反三,发现还是由于“关系”(links$relation
)属性不是“因子”导致的。
我们把 relation
属性转换成因子类型。
links$relation <- as.factor(links$relation)
links$color <- c("green", "red")[links$relation]
visNetwork(nodes, links)
太棒了!
下面,我们还是以年级作为交互选项,加入进来。
links$relation <- as.factor(links$relation)
links$color <- c("green", "red")[links$relation]
visNetwork(nodes, links) %>%
visOptions(selectedBy = "grade")
我们选择一下年级,拖动节点看看:
以上,是样例代码中,出现的内容解释。
小结
通过本文的学习,相信你已经掌握如何把社会网络的表格数据,用交互可视化的方法展现出来。
对于不同的元素,你也已经学会了基本的展现方式。
但请你一定不要止步于此。
首先,你需要继续查看文档。里面还有很多选项参数,此处我们没有涉及。例如说,这里我们只绘制了“关系”(边),但是却没有在其上进行任何文字标记。
如果你需要生成非常严肃的数据可视化作品,这些内容很可能用得上。请你点击这个链接,查看完整的文档。从中选择自己感兴趣的部分深入研读。
另外,这个工具,也绝不仅仅可以帮助你绘制社会网络图。只要是适合用网络图展现的内容,它都可以发挥作用。
希望你充分发挥自己举一反三的能力,把这项新技能用好。
祝学习愉快!
延伸阅读
你可能也会对以下话题感兴趣。点击链接就可以查看。
如何用R分析CNKI文献关键词词频?
在为学生找寻这款工具的时候,我还发现了不少其他的优质工具和教程资源。近期会发布在知识星球中。如果你订阅了,欢迎经常来看看,并且积极参与到我们的社区讨论中来。
如果你对社会网络分析感兴趣,推荐这本书给你。
喜欢请点赞和打赏。还可以微信关注和置顶我的公众号“玉树芝兰”(nkwangshuyi)。
如果你对 Python 与数据科学感兴趣,不妨阅读我的系列教程索引贴《如何高效入门数据科学?》,里面还有更多的有趣问题及解法。
由于微信公众号外部链接的限制,文中的部分链接可能无法正确打开。如有需要,请点击文末的“阅读原文”按钮,访问可以正常显示外链的版本。
知识星球入口在这里: